﻿using System;
using System.Runtime.InteropServices;
using SlimDX;

namespace GamepadDriver
{
	public static class MouseController
	{
		public const int INPUT_MOUSE = 0;
		public const int INPUT_KEYBOARD = 1;
		public const int INPUT_HARDWARE = 2;
		public const uint KEYEVENTF_EXTENDEDKEY = 0x0001;
		public const uint KEYEVENTF_KEYUP = 0x0002;
		public const uint KEYEVENTF_UNICODE = 0x0004;
		public const uint KEYEVENTF_SCANCODE = 0x0008;
		public const uint XBUTTON1 = 0x0001;
		public const uint XBUTTON2 = 0x0002;
		public const uint MOUSEEVENTF_MOVE = 0x0001;
		public const uint MOUSEEVENTF_LEFTDOWN = 0x0002;
		public const uint MOUSEEVENTF_LEFTUP = 0x0004;
		public const uint MOUSEEVENTF_RIGHTDOWN = 0x0008;
		public const uint MOUSEEVENTF_RIGHTUP = 0x0010;
		public const uint MOUSEEVENTF_MIDDLEDOWN = 0x0020;
		public const uint MOUSEEVENTF_MIDDLEUP = 0x0040;
		public const uint MOUSEEVENTF_XDOWN = 0x0080;
		public const uint MOUSEEVENTF_XUP = 0x0100;
		public const uint MOUSEEVENTF_WHEEL = 0x0800;
		public const uint MOUSEEVENTF_VIRTUALDESK = 0x4000;
		public const uint MOUSEEVENTF_ABSOLUTE = 0x8000;

		public const int WHEEL_DELTA = 120;

		[DllImport("user32.dll", SetLastError = true)]
		public static extern uint SendInput(uint nInputs, ref INPUT pInputs, int cbSize);

		public static void MoveBy(Vector2 amount)
		{
			MoveBy(amount.X, amount.Y);
		}

		public static void MoveBy<T>(T x, T y)
		{
			INPUT aInput = new INPUT();

			aInput.type = INPUT_MOUSE;
			aInput.mkhi.mi.dwFlags = MOUSEEVENTF_MOVE;
			aInput.mkhi.mi.dwExtraInfo = 0;
			aInput.mkhi.mi.mouseData = 0;
			aInput.mkhi.mi.time = 0;
			aInput.mkhi.mi.dx = Convert.ToInt32(x);
			aInput.mkhi.mi.dy = Convert.ToInt32(y);

			uint aResult = SendInput(1, ref aInput, Marshal.SizeOf(new INPUT()));
		}

		public static void MoveWheelBy<T>(T x)
		{
			INPUT aInput = new INPUT();

			aInput.type = INPUT_MOUSE;
			aInput.mkhi.mi.dwFlags = MOUSEEVENTF_WHEEL;
			aInput.mkhi.mi.dwExtraInfo = 0;
			aInput.mkhi.mi.mouseData = Convert.ToInt32(x) * WHEEL_DELTA;
			aInput.mkhi.mi.time = 0;

			Console.WriteLine("Wheel: {0}", aInput.mkhi.mi.mouseData);

			uint aResult = SendInput(1, ref aInput, Marshal.SizeOf(new INPUT()));
		}

		public static void Click(MouseButton button)
		{
			UseButton(button, ButtonState.Pressed);
			UseButton(button, ButtonState.Released);
		}

		public static void UseButton(MouseButton button, ButtonState state)
		{
			INPUT aInput = new INPUT();
			aInput.type = INPUT_MOUSE;
			aInput.mkhi.mi.dwExtraInfo = 0;
			aInput.mkhi.mi.mouseData = 0;
			aInput.mkhi.mi.time = 0;

			uint flag = 0;

			switch (button)
			{
				case MouseButton.Left:
					switch (state)
					{
						case ButtonState.Pressed:
							flag = MOUSEEVENTF_LEFTDOWN;
							break;
						case ButtonState.Released:
							flag = MOUSEEVENTF_LEFTUP;
							break;
					}
					break;
				case MouseButton.Middle:
					switch (state)
					{
						case ButtonState.Pressed:
							flag = MOUSEEVENTF_MIDDLEDOWN;
							break;
						case ButtonState.Released:
							flag = MOUSEEVENTF_MIDDLEUP;
							break;
					}
					break;
				case MouseButton.Right:
					switch (state)
					{
						case ButtonState.Pressed:
							flag = MOUSEEVENTF_RIGHTDOWN;
							break;
						case ButtonState.Released:
							flag = MOUSEEVENTF_RIGHTUP;
							break;
					}
					break;
			}

			aInput.mkhi.mi.dwFlags = flag;
			uint aResult = SendInput(1, ref aInput, Marshal.SizeOf(new INPUT()));
		}
	}
}